package bsh;

import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import javassist.bytecode.Opcode;
import jmine.tec.utils.debug.impl.DebugRuntimeFactory;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.util.ASMifierClassVisitor;
import org.objectweb.asm.util.TraceClassVisitor;

import bsh.asm.acessor.LocalObjectAccessor;
import bsh.asm.acessor.ObjectAccessor;
import bsh.asm.acessor.ThisFieldObjectAcessor;
import bsh.compilation.AbstractCompilationSource;
import bsh.compilation.CompilationOptions;
import bsh.compilation.CompilationResults;
import bsh.compilation.CompilationSource;
import bsh.compilation.DebugOptions;
import bsh.compilation.DefaultCompilationOptions;
import bsh.compilation.ReflectionUtilsImpl;
import bsh.exception.CompilationAssertionFailed;
import bsh.exception.CompilationFailedException;
import bsh.meta.DeclaredConstructor;
import bsh.meta.FieldDescriptor;
import bsh.meta.InnerTypeGlobalScope;
import bsh.meta.JavaClassWrapper;
import bsh.meta.MethodDescriptor;
import bsh.meta.MethodScope;
import bsh.meta.Scope;
import bsh.meta.TypeTransformation;
import bsh.meta.TypeWrapper;
import bsh.meta.UnfinishedInnerType;
import bsh.meta.UnfinishedMethodDescriptor;
import bsh.meta.UnfinishedType;
import bsh.meta.UnfinishedTypeImpl;
import bsh.utils.reflection.BytecodeHelper;
import bsh.utils.reflection.OpenClassLoader;
import bsh.utils.reflection.transform.DynamicTypeTransformationFactory;

/**
 * Implementacao de {@link Compiler} e {@link CompilerManagerInternals}
 * 
 * @author takeshi
 */
public class CompilerImpl implements CompilerManagerInternals, Opcodes, Compiler {

    private static final String EXECUTE_SCRIPT_METHOD_NAME = "executeScript";

    private static final String INVOCATION_CACHE_FIELD_NAME = "__invocationCache";

    private final OpenClassLoader classLoader = new OpenClassLoader();

    private final ReflectionUtils reflect = new ReflectionUtilsImpl();

    private final CompilationOptions compilationOptions = new DefaultCompilationOptions();

    /**
     * {@inheritDoc}
     */
    public Class<? extends Prototype> compile(String script, Map<String, Object> fields, String scriptName) throws ParseException,
            CompilationFailedException {
        String sourceCode = "generated.bsh";
        return this.compile(script, fields, scriptName, sourceCode);
    }

    /**
     * {@inheritDoc}
     */
    public Class<? extends Prototype> compile(String script, Map<String, Object> fields, String scriptName, String sourceCode,
            TypeExtractor extractor) throws ParseException, CompilationFailedException {
        Set<Entry<String, Object>> entrySet = fields.entrySet();
        Map<String, Class<?>> fieldTypes = new HashMap<String, Class<?>>(fields.size());
        for (Entry<String, Object> entry : entrySet) {
            fieldTypes.put(entry.getKey(), Object.class); // unknown types must be object
        }
        return this.compile(script, scriptName, sourceCode, fieldTypes, Prototype.class);
    }

    /**
     * {@inheritDoc}
     * 
     * @see bsh.CompilerManagerInternals#compile(java.lang.String, java.lang.String, java.lang.String, java.util.Map, java.lang.Class)
     */
    @SuppressWarnings("unchecked")
    public <E extends Prototype> Class<? extends E> compile(final String script, final String scriptName, final String sourceCode,
            final Map<String, Class<?>> fieldTypes, final Class<E> superType) throws ParseException, CompilationFailedException {
        CompilationSource source =
                new AbstractCompilationSource(scriptName, fieldTypes, new HashMap<String, Object>(), sourceCode, superType) {
                    public Reader openReader() {
                        return new StringReader(script);
                    }
                };
        CompilationResults results = this.compileOverrideOptions(source, this.compilationOptions);

        byte[] data = results.getBytecode();
        // this.writeToFile(sourceCode, data);
        return (Class<? extends E>) this.classLoader.defineClass(source.className(), data);
    }

    /**
     * Cria fields para o mapa passado
     * 
     * @param fieldTypes {@link Map} de {@link String} para {@link Class} com as definicoes de campos
     * @param unit {@link CompilationUnit}
     * @param writer {@link ClassWrapperTest}
     */
    private void createFields(Map<String, Class<?>> fieldTypes, CompilationUnit unit, ClassVisitor writer) {
        FieldDescriptor dynamicsField = unit.thisType().getDynamicsField();
        if (dynamicsField != null) {
            FieldVisitor f =
                    writer.visitField(Modifier.PUBLIC, dynamicsField.fieldName(), dynamicsField.fieldType().typeDescription(), null, null);
            f.visitEnd();
        }
        FieldDescriptor mathContextField = unit.thisType().getMathContextField();
        if (mathContextField != null) {
            FieldVisitor f =
                    writer.visitField(Modifier.PUBLIC, mathContextField.fieldName(), mathContextField.fieldType().typeDescription(), null,
                            null);
            f.visitEnd();
        }
        for (Entry<String, Class<?>> entry : fieldTypes.entrySet()) {
            FieldVisitor f = writer.visitField(Modifier.PUBLIC, entry.getKey(), Type.getDescriptor(entry.getValue()), null, null);
            f.visitEnd();
            unit.addField(entry.getKey(), JavaClassWrapper.wrapperFor(entry.getValue()), Opcodes.ACC_PUBLIC);
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @see bsh.CompilerManagerInternals#compile(java.lang.String, java.util.Map, java.lang.String, java.lang.String)
     */
    public Class<? extends Prototype> compile(String script, Map<String, Object> fields, String scriptName, String sourceCode)
            throws ParseException, CompilationFailedException {
        return this.compile(script, fields, scriptName, sourceCode, new TypeExtractor() {

            public Class<?> extractTypeOf(String name, Object value) {
                return Object.class;
            }
        });
    }

    /**
     * Cria um ConcurrentHashMap estatico final na classe e inicializa com um bloco de inicializacao estatico.
     * 
     * @param cw ClassVisitor
     * @param clName String o nome da classe
     * @param constants Outras inicializacoes estaticas
     * @param unit {@link CompilationUnit}
     * @throws CompilationFailedException e
     */
    private void createStaticCallsiteCache(ClassVisitor cw, String clName, List<BytecodeGenerator> constants, CompilationUnit unit)
            throws CompilationFailedException {
        FieldVisitor fv = cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, INVOCATION_CACHE_FIELD_NAME, "Ljava/util/Map;", null, null);
        fv.visitEnd();
        CompilationState state = new CompilationState(unit);

        MethodVisitor mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
        mv.visitCode();
        mv.visitTypeInsn(NEW, "java/util/concurrent/ConcurrentHashMap");
        mv.visitInsn(DUP);
        mv.visitMethodInsn(INVOKESPECIAL, "java/util/concurrent/ConcurrentHashMap", "<init>", "()V");
        mv.visitFieldInsn(PUTSTATIC, clName, INVOCATION_CACHE_FIELD_NAME, "Ljava/util/Map;");
        for (BytecodeGenerator staticInitializer : constants) {
            staticInitializer.generateBytecode(mv, unit, state);
        }
        mv.visitInsn(RETURN);
        mv.visitMaxs(2, 0);
        mv.visitEnd();

        mv =
                cw.visitMethod(ACC_PUBLIC, "getInvocationCache", "()Ljava/util/Map;",
                        "()Ljava/util/Map<Lbsh/dyn/Callsite;Lbsh/dyn/Invocable;>;", null);
        mv.visitCode();
        mv.visitFieldInsn(GETSTATIC, clName, INVOCATION_CACHE_FIELD_NAME, "Ljava/util/Map;");
        mv.visitInsn(ARETURN);
        mv.visitMaxs(1, 1);
        mv.visitEnd();

    }

    /**
     * Adiciona as assinaturas dos metodos ao {@link CompilationUnit}
     * 
     * @param dec {@link List} de {@link BSHMethodDeclaration}
     * @param unit {@link CompilationUnit}
     * @throws CompilationFailedException e
     */
    private void addMethodSignatures(List<DelayedMethod> dec, CompilationUnit unit) throws CompilationFailedException {
        for (DelayedMethod method : dec) {
            MethodDeclarationParser parser = new MethodDeclarationParser(method.getMethodDeclaration(), unit, method.getDeclaringScope());
            MethodDescriptor methodDescriptor = parser.getMethodDescriptor();
            unit.addMethod(methodDescriptor);
        }
    }

    /**
     * Adiciona um Import ao {@link CompilationUnit}
     * 
     * @param unit {@link CompilationUnit}
     * @param n {@link BSHImportDeclaration}
     */
    private void addImport(CompilationUnit unit, BSHImportDeclaration n) {
        String text = ((BSHAmbiguousName) n.getChild(0)).text;
        if (n.importPackage) {
            unit.getGlobalScope().addWildcardImport(text);
        } else {
            TypeWrapper jType = this.reflectionUtils().resolveType(text);
            if (jType == null) {
                throw new CompilationAssertionFailed("cannot find type with name: " + text);
            }
            String alias = extractSuffix(text);
            unit.getGlobalScope().addTypeAlias(alias, jType);
        }
    }

    /**
     * @param text String
     * @return String
     */
    private static String extractSuffix(String text) {
        return text.substring(text.lastIndexOf('.') + 1, text.length());
    }

    /**
     * devolve o tipo primitivo dado um wrapper ou devolve o proprio wrapper
     * 
     * @param class1 Class
     * @return Class
     */
    public static Class<?> unboxType(Class<?> class1) {
        if (Primitive.isWrapperType(class1)) {
            return Primitive.unboxType(class1);
        }
        return class1;
    }

    /**
     * Cria o corpo do script no metodo {@link CompiledScript#executeScript()}
     * 
     * @param writer {@link ClassVisitor}
     * @param unit {@link CompilationUnit}
     * @param allNodes {@link List} de {@link SimpleNode}
     * @param source the {@link CompilationSource}
     * @return {@link List} de {@link BSHMethodDeclaration}
     * @throws CompilationFailedException e
     */
    private List<DelayedMethod> createScriptBody(ClassVisitor writer, CompilationUnit unit, List<SimpleNode> allNodes,
            CompilationSource source) throws CompilationFailedException {
        String[] exceptionNames = new String[]{ Type.getInternalName(Throwable.class) };
        TypeWrapper returnType = JavaClassWrapper.OBJECT_WRAPPER;
        Parameter[] params = new Parameter[0];

        if (!hasCode(allNodes) && typeImplementsExecuteScript(unit.thisType().superType())) {
            // make a super call
            MethodDescriptor method =
                    this.reflect.findExactMatch(unit.thisType().superType(), EXECUTE_SCRIPT_METHOD_NAME, new TypeWrapper[0]);
            final String methodDescriptor = Type.getMethodDescriptor(Type.getType(Object.class), new Type[0]);
            MethodVisitor visitor =
                    writer.visitMethod(removeModifier(method.modifiers(), Opcodes.ACC_ABSTRACT), "executeScript", methodDescriptor, null,
                            null);
            visitor.visitCode();

            visitor.visitVarInsn(Opcodes.ALOAD, 0);
            visitor.visitMethodInsn(Opcode.INVOKESPECIAL, unit.thisType().superType().internalName(), EXECUTE_SCRIPT_METHOD_NAME,
                    methodDescriptor);
            visitor.visitInsn(Opcodes.ARETURN);
            visitor.visitMaxs(0, 0);
            visitor.visitEnd();
            return collectMethodDeclarations(allNodes, unit.getGlobalScope());
        } else {
            BytecodeGenerator[] generators = new BytecodeGenerator[0];
            if (source != null) {
                List<BytecodeGenerator> list = this.collectInitializers(source);
                generators = list.toArray(new BytecodeGenerator[list.size()]);
            }
            return this.createMethod(writer, unit, allNodes, EXECUTE_SCRIPT_METHOD_NAME, exceptionNames, returnType, params,
                    new UnfinishedMethodDescriptor(new TypeWrapper[0], unit.thisType(), EXECUTE_SCRIPT_METHOD_NAME, new TypeWrapper[0],
                            returnType), unit.getGlobalScope(), generators);
        }
    }

    /**
     * Collect all method declarations from the given list of {@link SimpleNode}
     * 
     * @param allNodes all the nodes
     * @param globalScope the {@link Scope}
     * @return {@link List} of {@link DelayedMethod}
     */
    private List<DelayedMethod> collectMethodDeclarations(List<SimpleNode> allNodes, Scope globalScope) {
        List<DelayedMethod> ms = new ArrayList<DelayedMethod>();
        for (SimpleNode simpleNode : allNodes) {
            if (simpleNode instanceof BSHMethodDeclaration) {
                ms.add(new DelayedMethod((BSHMethodDeclaration) simpleNode, globalScope));
            }
        }
        return ms;
    }

    /**
     * Returns true if the given list of nodes has node that represents code
     * 
     * @param allNodes the list of nodes
     * @return boolean
     */
    private boolean hasCode(List<SimpleNode> allNodes) {
        for (SimpleNode simpleNode : allNodes) {
            if (!(simpleNode instanceof BSHMethodDeclaration) && !(simpleNode instanceof BSHImportDeclaration)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Verifies if the given type implements the {@link CompiledScript#executeScript()}
     * 
     * @param superType the type
     * @return boolean
     */
    private boolean typeImplementsExecuteScript(TypeWrapper superType) {
        Class<?> javaClass = JavaClassWrapper.unwrapJavaClass(superType);
        if (javaClass != null) {
            try {
                Method method = javaClass.getDeclaredMethod(EXECUTE_SCRIPT_METHOD_NAME);
                return !Modifier.isAbstract(method.getModifiers());
            } catch (NoSuchMethodException e) {
                return false;
            }
        }
        return false;
    }

    /**
     * Compila um metodo usando os nos passados. Devolve uma lista com metodos que estao declarados dentro deste para serem compilados
     * depois.
     * 
     * @param writer {@link ClassVisitor}
     * @param unit {@link CompilationUnit}
     * @param allNodes {@link List} de {@link SimpleNode}
     * @param methodName String
     * @param exceptionNames String[]
     * @param returnType {@link TypeWrapper}
     * @param params {@link Parameter}
     * @param method {@link MethodDescriptor}
     * @param declaringScope the declaring scope
     * @param pre bytecode to be put before the actual code - like initialization, etc
     * @return {@link List} de {@link BSHImportDeclaration} declarados dentro do metodo
     * @throws CompilationFailedException e
     */
    private List<DelayedMethod> createMethod(ClassVisitor writer, CompilationUnit unit, List<SimpleNode> allNodes, String methodName,
            String[] exceptionNames, TypeWrapper returnType, Parameter[] params, MethodDescriptor method, Scope declaringScope,
            BytecodeGenerator... pre) throws CompilationFailedException {
        List<DelayedMethod> innerMethods = new LinkedList<DelayedMethod>();
        String descriptor = this.toDescriptor(params, returnType);

        int lineNumber = allNodes.isEmpty() ? -1 : allNodes.get(0).getLineNumber();
        unit.thisType().writeMethod(lineNumber, methodName, method.parameterTypes());

        MethodVisitor visitor = writer.visitMethod(method.modifiers(), methodName, descriptor, null, exceptionNames);
        if (Modifier.isAbstract(method.modifiers())) {
            visitor.visitEnd();
            return Collections.emptyList();
        }
        visitor.visitCode();
        CompilationState state = new CompilationState(unit);

        // 0 is 'this'
        Scope scope = state.newMethodScope(method, declaringScope);

        for (BytecodeGenerator gen : pre) {
            gen.generateBytecode(visitor, unit, state);
            BlockNodeCompiler.checkStackEmpty(visitor, state);
        }
        state = new CompilationState(unit);
        scope = state.newMethodScope(method, declaringScope);
        // first, lets find the inner types, method and variable declarations
        for (SimpleNode node : allNodes) {
            if (node instanceof BSHClassDeclaration) {
                this.addUnfinishedInnerClassDeclaration(unit, (BSHClassDeclaration) node, scope);
            } else if (node instanceof BSHMethodDeclaration) {
                innerMethods.add(new DelayedMethod((BSHMethodDeclaration) node, scope));
            }
        }

        this.addMethodSignatures(innerMethods, unit);

        scope.installStartLabel(visitor, state.getUnit().options());
        for (int i = 0; i < params.length; i++) {
            LocalVariable variable = scope.addMethodParameter(params[i].getName(), params[i].getType());
            if (unit.options().debugOptions().contains(DebugOptions.VIRTUAL_STACK_FRAMES)) {
                TypedVariableDeclarationCompiler.setLocalVariable(variable.getType(), visitor, state, variable);
            }
        }

        state.assertEmptyStack("line number: " + lineNumber);
        SimpleNode last = null;
        for (SimpleNode simpleNode : allNodes) {
            last = simpleNode;
            if (simpleNode instanceof BSHMethodDeclaration) {
                continue;
            }
            popStackIfNeeded(visitor, state);
            NodeCompiler compiler = unit.getNodeCompiler(simpleNode);
            compiler.compile(visitor, state);
        }
        scope.installEndLabel(visitor, unit.options());
        if (JavaClassWrapper.VOID_WRAPPER.equals(returnType)) {
            popStackIfNeeded(visitor, state);
            state.assertEmptyStack();
            visitor.visitInsn(RETURN);
        } else {
            if (state.getStackSize() == 0) {
                // push null
                if (!(last instanceof BSHReturnStatement)) {
                    visitor.visitInsn(ACONST_NULL);
                    state.pushStack(TypeWrapper.NULL_TYPE_WRAPPER);
                } else {
                    state.popScope();
                    visitor.visitMaxs(0, 0);
                    visitor.visitEnd();
                    return innerMethods;
                }
            } else {
                state.assertStackSize(1);
                this.coerceToType(visitor, state.peekStack(), returnType, state);
            }
            state.assertStackSize(1);
            state.popStack();
            visitor.visitInsn(returnType.toASMType().getOpcode(Opcodes.IRETURN));
        }
        state.popScope();
        visitor.visitMaxs(0, 0);
        visitor.visitEnd();
        return innerMethods;
    }

    /**
     * Adds type aliases for inner classes.
     * 
     * @param unit the declared {@link CompilationUnit}
     * @param node the class declaration node
     * @param scope the declaring {@link Scope}
     * @throws CompilationFailedException e
     */
    private void addUnfinishedInnerClassDeclaration(CompilationUnit unit, BSHClassDeclaration node, Scope scope)
            throws CompilationFailedException {
        ClassDeclarationNodeCompiler nodeCompiler = (ClassDeclarationNodeCompiler) unit.getNodeCompiler(node);
        UnfinishedInnerType innerType = nodeCompiler.getUnfinishedType(scope);
        if (innerType.simpleName() != null) {
            scope.addTypeAlias(innerType.simpleName(), innerType);
        }
    }

    /**
     * Faz um pop no stack se necessario. Alguns nos podem deixar elementos no stack.
     * 
     * @param visitor {@link MethodVisitor}
     * @param state {@link CompilationState}
     */
    public static void popStackIfNeeded(MethodVisitor visitor, CompilationState state) {
        if (state.getStackSize() > 0) {
            state.assertStackSize(1);
            state.popStack(visitor);
        }
    }

    /**
     * Devolve o descritor de um metodo com os parametros e tipo de retorno passados, como requerido pela JLS
     * 
     * @param params {@link Parameter}
     * @param returnType {@link TypeWrapper}
     * @return String
     */
    private String toDescriptor(Parameter[] params, TypeWrapper returnType) {
        Type[] args = new Type[params.length];
        for (int i = 0; i < args.length; i++) {
            args[i] = params[i].getType().toASMType();
        }
        return Type.getMethodDescriptor(returnType.toASMType(), args);
    }

    /**
     * Faz o boxing de uma primitiva se o topo do stack contiver uma primitiva
     * 
     * @param visitor {@link MethodVisitor}
     * @param state {@link CompilationState}
     */
    public static void boxPrimitiveIfNecessary(MethodVisitor visitor, CompilationState state) {
        TypeWrapper type = state.peekStack();
        Class<?> javaType = JavaClassWrapper.unwrapJavaClass(type);
        if (javaType instanceof Class<?> && ((Class<?>) javaType).isPrimitive()) {
            BytecodeHelper.autoBox(visitor, javaType);
        }
    }

    /**
     * Create the declared methods, adding the method descriptor and actually writing the method to the given {@link ClassVisitor}
     * 
     * @param writer {@link ClassVisitor}
     * @param dec {@link List} de {@link BSHMethodDeclaration}
     * @param unit {@link CompilationUnit}
     * @throws CompilationFailedException e
     */
    private void createDeclaredMethods(ClassVisitor writer, List<DelayedMethod> dec, CompilationUnit unit)
            throws CompilationFailedException {
        List<DelayedMethod> lateMethods = new LinkedList<DelayedMethod>();
        for (DelayedMethod method : dec) {
            MethodDeclarationParser parser = new MethodDeclarationParser(method.getMethodDeclaration(), unit, method.getDeclaringScope());
            if (parser.isConstructor()) {
                throw new CompilationFailedException(method.getMethodDeclaration().getLineNumber(), "Misplaced constructor");
            } else {
                MethodDescriptor methodDescriptor = parser.getMethodDescriptor();
                unit.addMethod(methodDescriptor);
                List<SimpleNode> body = this.getMethodBodyChildren(parser.getMethodBlock());
                lateMethods.addAll(this.createMethod(writer, unit, body, methodDescriptor.methodName(), new String[0],
                        methodDescriptor.returnType(), parser.getParams(), methodDescriptor, method.getDeclaringScope()));
            }
        }
        if (!lateMethods.isEmpty()) {
            // create other methods
            this.createDeclaredMethods(writer, lateMethods, unit);
        }
    }

    /**
     * Devolve os "filhos" de um metodo.
     * 
     * @param methodBlock BSHBlock
     * @return {@link List}
     */
    private List<SimpleNode> getMethodBodyChildren(BSHBlock methodBlock) {
        if (methodBlock == null) {
            // abstract method
            return Collections.emptyList();
        }
        int offset = 0;
        if (methodBlock.isSynchronized) {
            offset++;
        }
        List<SimpleNode> list = new ArrayList<SimpleNode>(methodBlock.jjtGetNumChildren() - offset);
        for (int i = offset; i < methodBlock.jjtGetNumChildren(); i++) {
            list.add(methodBlock.getChild(i));
        }
        return list;
    }

    /**
     * Cria o construtor para a classe
     * 
     * @param cw {@link ClassVisitor}
     * @param unit the {@link CompilationUnit}
     * @param class1 a superclasse
     * @param source the {@link CompilationSource}
     * @throws CompilationFailedException e
     */
    private void emmitConstructor(ClassVisitor cw, Class<?> class1, CompilationUnit unit, CompilationSource source)
            throws CompilationFailedException {
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
        mv.visitCode();
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(class1), "<init>", "()V");

        this.initializeSyntheticFields(unit, mv);

        mv.visitInsn(RETURN);
        mv.visitMaxs(1, 1);
        mv.visitEnd();
    }

    /**
     * Collect all the initializers from all the traits
     * 
     * @param source {@link CompilationSource}
     * @return {@link List} of {@link BytecodeGenerator}
     */
    private List<BytecodeGenerator> collectInitializers(CompilationSource source) {
        List<BytecodeGenerator> list = new ArrayList<BytecodeGenerator>();
        for (Trait t : source.implementedTraits()) {
            collectInitializersRecurse(t, list);
        }
        return list;
    }

    /**
     * Collect all the initializers from the given trait, recursing into dependent traits
     * 
     * @param trait the given {@link Trait}
     * @param list {@link List} of {@link BytecodeGenerator}
     * @return {@link List}
     */
    private List<BytecodeGenerator> collectInitializersRecurse(Trait trait, List<BytecodeGenerator> list) {
        list.addAll(trait.instanceInitializers());
        for (Trait dep : trait.implementedTraits()) {
            collectInitializersRecurse(dep, list);
        }
        return list;
    }

    /**
     * Adds instance initializers from a given trait
     * 
     * @param unit the {@link CompilationUnit}
     * @param source the {@link CompilationSource}
     * @param mv the {@link MethodVisitor}
     * @throws CompilationFailedException e
     */
    public void addInstanceInitializersFromTraits(CompilationUnit unit, CompilationSource source, MethodVisitor mv)
            throws CompilationFailedException {
        CompilationState state = new CompilationState(unit);
        List<LoadedTrait> traits = source.implementedTraits();
        for (LoadedTrait loadedTrait : traits) {
            addInitializersFrom(loadedTrait, mv, unit, state);
        }
    }

    /**
     * Adds the initializer code from a given {@link LoadedTrait}
     * 
     * @param loadedTrait {@link LoadedTrait}
     * @param mv {@link MethodVisitor}
     * @param unit the {@link CompilationUnit}
     * @param state {@link CompilationState}
     * @throws CompilationFailedException e
     */
    private void addInitializersFrom(LoadedTrait loadedTrait, MethodVisitor mv, CompilationUnit unit, CompilationState state)
            throws CompilationFailedException {
        int stack = state.getStackSize();
        for (LoadedTrait dependency : loadedTrait.implementedTraits()) {
            addInitializersFrom(dependency, mv, unit, state);
        }
        state.assertStackSize(stack);
        for (BytecodeGenerator gen : loadedTrait.instanceInitializers()) {
            gen.generateBytecode(mv, unit, state);
            if (state.getStackSize() > stack) {
                popStackIfNeeded(mv, state);
            }
        }
        state.assertStackSize(stack);
    }

    /**
     * Initializes synthetic fields
     * 
     * @param unit the {@link CompilationUnit}
     * @param mv the {@link MethodVisitor}
     */
    public void initializeSyntheticFields(CompilationUnit unit, MethodVisitor mv) {
        FieldDescriptor dynamicsField = unit.thisType().getDynamicsField();
        if (dynamicsField != null) {
            mv.visitVarInsn(ALOAD, 0);
            mv.visitTypeInsn(NEW, "java/util/concurrent/ConcurrentHashMap");
            mv.visitInsn(DUP);
            mv.visitMethodInsn(INVOKESPECIAL, "java/util/concurrent/ConcurrentHashMap", "<init>", "()V");
            mv.visitFieldInsn(PUTFIELD, unit.thisType().internalName(), dynamicsField.fieldName(), "Ljava/util/Map;");
        }
        FieldDescriptor math = unit.thisType().getMathContextField();
        if (math != null) {
            mv.visitVarInsn(ALOAD, 0);
            mv.visitFieldInsn(GETSTATIC, "java/math/MathContext", "DECIMAL64", "Ljava/math/MathContext;");
            mv.visitFieldInsn(PUTFIELD, unit.thisType().internalName(), math.fieldName(), "Ljava/math/MathContext;");
        }
    }

    /**
     * {@inheritDoc}
     */
    public NodeCompiler getNodeCompiler(SimpleNode node, CompilationUnit unit) throws CompilationFailedException {
        if (node instanceof BSHMethodInvocation) {
            return new MethodCallCompiler((BSHMethodInvocation) node, unit);
        } else if (node instanceof BSHPrimaryExpression) {
            return new PrimaryExpressionCompiler((BSHPrimaryExpression) node, unit);
        } else if (node instanceof BSHAmbiguousName) {
            return new AmbiguosNameCompiler((BSHAmbiguousName) node, unit);
        } else if (node instanceof BSHPrimarySuffix) {
            return new PrimarySuffixCompiler((BSHPrimarySuffix) node, unit);
        } else if (node instanceof BSHLiteral) {
            return new LiteralCompiler((BSHLiteral) node, this.reflect);
        } else if (node instanceof BSHTypedVariableDeclaration) {
            return new TypedVariableDeclarationCompiler((BSHTypedVariableDeclaration) node, unit);
        } else if (node instanceof BSHAssignment) {
            return new AssignmentNodeCompiler((BSHAssignment) node, unit);
        } else if (node instanceof BSHUnaryExpression) {
            return new UnaryOperationNodeCompiler((BSHUnaryExpression) node, unit);
        } else if (node instanceof BSHFormalComment) {
            return new CommentAsLDCCompiler((BSHFormalComment) node, unit);
        } else if (node instanceof BSHReturnStatement) {
            return new ReturnStatementCompiler((BSHReturnStatement) node, unit);
        } else if (node instanceof BSHWhileStatement) {
            return new WhileStatementCompiler((BSHWhileStatement) node, unit);
        } else if (node instanceof BSHBlock) {
            return new BlockNodeCompiler((BSHBlock) node, unit);
        } else if (node instanceof BSHBinaryExpression) {
            return new BinaryExpressionCompiler((BSHBinaryExpression) node, unit);
        } else if (node instanceof BSHForStatement) {
            return new ForNodeCompiler((BSHForStatement) node, unit);
        } else if (node instanceof BSHStatementExpressionList) {
            return new StatementListNodeCompiler((BSHStatementExpressionList) node, unit);
        } else if (node instanceof BSHIfStatement) {
            return new IfNodeCompiler((BSHIfStatement) node, unit);
        } else if (node instanceof BSHAllocationExpression) {
            return new AllocationNodeCompiler((BSHAllocationExpression) node, unit);
        } else if (node instanceof BSHCastExpression) {
            return new TypeCastCompiler((BSHCastExpression) node, unit);
        } else if (node instanceof BSHThrowStatement) {
            return new ThrowNodeCompiler((BSHThrowStatement) node, unit);
        } else if (node instanceof BSHTernaryExpression) {
            return new TernaryExpressionCompiler((BSHTernaryExpression) node, unit);
        } else if (node instanceof BSHType) {
            return new TypeLiteralNodeCompiler((BSHType) node);
        } else if (node instanceof BSHTryStatement) {
            return new TryCatchFinallyCompiler((BSHTryStatement) node, unit);
        } else if (node instanceof BSHEnhancedForStatement) {
            return new EnhancedForCompiler(unit, (BSHEnhancedForStatement) node);
        } else if (node instanceof BSHClassDeclaration) {
            return new ClassDeclarationNodeCompiler(unit, (BSHClassDeclaration) node);
        } else if (node instanceof BSHSwitchStatement) {
            return new SwitchNodeCompiler((BSHSwitchStatement) node);
        } else if (node instanceof BSHArrayInitializer) {
            return new ArrayInitializerNodeCompiler((BSHArrayInitializer) node, unit);
        }
        throw new NoSuchMethodError("compiler not implemented for: " + node.getClass() + " at line: " + node.getLineNumber());
    }

    /**
     * {@inheritDoc}
     */
    public ReflectionUtils reflectionUtils() {
        return this.reflect;
    }

    /**
     * {@inheritDoc}
     */
    public void coerceToType(MethodVisitor host, TypeWrapper fromType, TypeWrapper targetType, CompilationState state)
            throws CompilationFailedException {
        this.coerceToType(host, fromType, targetType, state, ReflectionUtils.MAX_DEPTH);
    }

    /**
     * Executa os {@link TypeTransformation} contidos na lista
     * 
     * @param host {@link MethodVisitor}
     * @param fromType {@link TypeWrapper}
     * @param state {@link CompilationState}
     * @param list {@link List} de {@link TypeTransformation}
     * @throws CompilationFailedException e
     */
    private void performTypeTransformations(MethodVisitor host, TypeWrapper fromType, CompilationState state, List<TypeTransformation> list)
            throws CompilationFailedException {
        int oldStackSize = state.getStackSize();
        TypeWrapper last = fromType;
        for (TypeTransformation typeTransformation : list) {
            last = typeTransformation.transformStackTop(host, last, state);
        }
        state.assertStackSize(oldStackSize);
    }

    /**
     * {@inheritDoc}
     * 
     * @see bsh.CompilerManagerInternals#coerceToType(org.objectweb.asm.MethodVisitor, bsh.meta.TypeWrapper, bsh.meta.TypeWrapper,
     * bsh.CompilationState, int)
     */
    public void coerceToType(MethodVisitor host, TypeWrapper fromType, TypeWrapper targetType, CompilationState state, int maxDepth)
            throws CompilationFailedException {
        if (fromType.equals(targetType)) {
            // nothing to do
            return;
        }
        for (int i = 1; i <= maxDepth; i++) {
            List<TypeTransformation> list =
                    this.reflect.findTransformationMatchingIncluding(fromType, targetType, new DynamicTypeTransformationFactory());
            if (list != null) {
                this.performTypeTransformations(host, fromType, state, list);
                return;
            }
        }
        throw new CompilationAssertionFailed("cannot coerce from " + fromType + " to " + targetType);
    }

    /**
     * {@inheritDoc}
     * 
     * @see bsh.CompilerManagerInternals#unbox(java.lang.Class, org.objectweb.asm.MethodVisitor, bsh.CompilationState)
     */
    public void unbox(Class<?> type, MethodVisitor host, CompilationState state) {
        if (Primitive.isWrapperType(type)) {
            state.popStack();
            BytecodeHelper.autoUnbox(host, Primitive.unboxType(type));
            state.pushStack(Primitive.unboxType(type));
        } else {
            throw new CompilationAssertionFailed("cannot unbox non wrapper of type: " + type);
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @see bsh.CompilerManagerInternals#coerceStackTopTo(org.objectweb.asm.MethodVisitor, bsh.meta.TypeWrapper, bsh.CompilationState)
     */
    public void coerceStackTopTo(MethodVisitor host, TypeWrapper typeWrapper, CompilationState state) throws CompilationFailedException {
        this.coerceToType(host, state.peekStack(), typeWrapper, state);
    }

    /**
     * {@inheritDoc}
     * 
     * @see bsh.Compiler#compile(bsh.compilation.CompilationSource)
     */
    public CompilationResults compile(CompilationSource source) throws ParseException, CompilationFailedException {
        return this.compileOverrideOptions(source, this.compilationOptions);
    }

    /**
     * {@inheritDoc}
     * 
     * @see bsh.Compiler#compileOverrideOptions(bsh.compilation.CompilationSource, bsh.compilation.CompilationOptions)
     */
    public CompilationResults compileOverrideOptions(CompilationSource source, CompilationOptions options) throws ParseException,
            CompilationFailedException {
        final String clName = source.internalName().replaceAll("[.]", "/");
        CompilationUnit unit = createCompilationUnit(this.createUnfinishedType(source, ACC_PUBLIC), source, options, clName, ACC_PUBLIC);

        for (Trait trait : source.implementedTraits()) {
            for (String wild : trait.allWildcardImports()) {
                unit.getGlobalScope().addWildcardImport(wild);
            }
            for (Entry<String, Class<?>> entry : trait.allTypeImports().entrySet()) {
                unit.getGlobalScope().addTypeAlias(entry.getKey(), JavaClassWrapper.wrapperFor(entry.getValue()));
            }
        }

        ClassWriter writer = initializeClassWriter(source, unit, ACC_PUBLIC | ACC_SUPER);

        ClassVisitor visitor = writer;

        if (DebugRuntimeFactory.ENABLED && DebugRuntimeFactory.getInstance().getPropertyAsBoolean("bsh.compiler.trace")) {
            visitor =
                    new DoubleClassVisitor(writer, new TraceClassVisitor(new ASMifierClassVisitor(new PrintWriter(System.out)),
                            new PrintWriter(System.out)));
        }
        long parseTime, compileTime;
        try {
            List<BytecodeGenerator> constants = this.createConstants(visitor, unit, source.constants());
            Map<String, Class<?>> map = source.fieldTypes();
            Set<String> keySet = source.constants().keySet();
            for (String string : keySet) {
                map.remove(string);
            }

            this.createFields(map, unit, visitor);
            addFieldsFromTraits(source.implementedTraits(), unit, visitor);
            this.createStaticCallsiteCache(visitor, unit.thisType().internalName(), constants, unit);
            this.createDebugFlags(visitor, options.debugOptions());
            this.emmitConstructor(visitor, source.superType(), unit, source);

            List<SimpleNode> allNodes = new ArrayList<SimpleNode>();
            long t0 = System.nanoTime();
            final Reader reader = source.openReader();
            Parser parser = new Parser(reader);
            boolean eof = false;
            while (!eof) {
                eof = parser.Line();
                if (parser.jjtree.nodeArity() > 0) {
                    SimpleNode n = (SimpleNode) parser.jjtree.popNode();
                    if (n instanceof BSHImportDeclaration) {
                        this.addImport(unit, (BSHImportDeclaration) n);
                    } else {
                        allNodes.add(n);
                    }
                }
            }
            parseTime = System.nanoTime() - t0;
            t0 = System.nanoTime();

            CompiledScriptPrototypeWriter prototype = new CompiledScriptPrototypeWriter();
            prototype.write(visitor, new CompilationState(unit));
            List<DelayedMethod> list = this.createScriptBody(visitor, unit, allNodes, source);
            if (list.size() > 0) {
                this.createDeclaredMethods(visitor, list, unit);
            }
            addMethodsFromTraits(source.implementedTraits(), unit, visitor);
            for (FieldDescriptor fd : unit.thisType().getDelayedFields()) {
                visitor.visitField(fd.asmModifiers(), fd.fieldName(), fd.fieldType().toASMType().getDescriptor(), null, null);
            }
            compileTime = System.nanoTime() - t0;
        } finally {
            visitor.visitEnd();
        }
        byte[] data = writer.toByteArray();
        return new CompilationResults(data, TimeUnit.NANOSECONDS.toMillis(parseTime), TimeUnit.NANOSECONDS.toMillis(compileTime), unit
                .thisType().internalName(), unit.getDependentTypes());
    }

    /**
     * Creates an {@link UnfinishedType}
     * 
     * @param source the {@link CompilationSource}
     * @param classModifiers int
     * @param interfaces the inmplemented interfaces
     * @return {@link UnfinishedType}
     */
    private UnfinishedType createUnfinishedType(CompilationSource source, int classModifiers, TypeWrapper... interfaces) {
        TypeWrapper superType;
        if ((classModifiers & ACC_INTERFACE) != 0) {
            superType = JavaClassWrapper.OBJECT_WRAPPER;
        } else {
            superType = JavaClassWrapper.wrapperFor(source.superType());
        }
        return new UnfinishedTypeImpl(source.sourceCodeName(), source.className(), superType, Arrays.asList(interfaces), classModifiers);
    }

    /**
     * Creates a {@link CompilationUnit}
     * 
     * @param unfinishedType the {@link UnfinishedType}
     * @param source {@link CompilationSource}
     * @param options {@link CompilationOptions}
     * @param clName the class name
     * @param classModifiers the class modifiers
     * @return {@link CompilationUnit}
     */
    public CompilationUnit createCompilationUnit(UnfinishedType unfinishedType, CompilationSource source, CompilationOptions options,
            final String clName, int classModifiers) {
        CompilationUnit unit1 = new CompilationUnitImpl(source.sourceCodeName(), clName, this, options, classModifiers, unfinishedType);
        unit1.getGlobalScope().addWildcardImport("java.util.*");
        unit1.getGlobalScope().addWildcardImport("java.lang.*");
        unit1.getGlobalScope().addTypeAlias("BigDecimal", JavaClassWrapper.BIG_DECIMAL_WRAPPER);

        Set<Entry<String, Class<?>>> set = source.typeAlias().entrySet();
        for (Entry<String, Class<?>> entry : set) {
            unit1.getGlobalScope().addTypeAlias(entry.getKey(), JavaClassWrapper.wrapperFor(entry.getValue()));
        }

        for (String string1 : source.wildcardImports()) {
            unit1.getGlobalScope().addWildcardImport(string1);
        }

        Set<Entry<String, Class<?>>> entrySet = source.typeAlias().entrySet();
        for (Entry<String, Class<?>> entry : entrySet) {
            unit1.getGlobalScope().addTypeAlias(entry.getKey(), JavaClassWrapper.wrapperFor(entry.getValue()));
        }
        return unit1;
    }

    /**
     * Initializes a {@link ClassWriter}
     * 
     * @param source the {@link CompilationSource}
     * @param unit the {@link CompilationUnit}
     * @param modifiers the class modifiers
     * @return {@link ClassWriter}
     */
    public ClassWriter initializeClassWriter(CompilationSource source, CompilationUnit unit, int modifiers) {
        ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        Set<String> interfaces = this.extractInterfaces(unit);
        addInterfacesFromCompilationSource(source, interfaces);
        writer.visit(Opcodes.V1_5, modifiers, unit.thisType().internalName(), null, unit.thisType().superType().internalName(),
                interfaces.toArray(new String[interfaces.size()]));
        if (unit.thisType().getSourceCodeFileName() != null) {
            writer.visitSource(unit.thisType().getSourceCodeFileName(), null);
        }
        return writer;
    }

    /**
     * Adds methods from the given traits
     * 
     * @param traits the list of traits
     * @param unit the {@link CompilationUnit}
     * @param writer {@link ClassVisitor}
     * @throws CompilationFailedException e
     */
    private void addMethodsFromTraits(List<? extends LoadedTrait> traits, CompilationUnit unit, ClassVisitor writer)
            throws CompilationFailedException {
        LinkedList<LoadedTrait> allTraits = new LinkedList<LoadedTrait>(traits);
        while (!allTraits.isEmpty()) {
            final LoadedTrait trait = allTraits.removeFirst();
            for (Method m : trait.traitInterface().getMethods()) {
                addMethodFromTrait(m, trait, unit, writer);
            }
            allTraits.addAll(trait.implementedTraits());
        }
    }

    /**
     * Adds a method from a trait if a method with the same signature is not implemented
     * 
     * @param m the Method
     * @param trait the {@link LoadedTrait} that contains the method
     * @param unit the {@link CompilationUnit}
     * @param writer the {@link ClassVisitor}
     * @throws CompilationFailedException e
     */
    private void addMethodFromTrait(Method m, LoadedTrait trait, CompilationUnit unit, ClassVisitor writer)
            throws CompilationFailedException {
        final TypeWrapper[] parameterTypes = toTypeWrappers(m.getParameterTypes());
        List<TypeWrapper> typeParameters = Arrays.asList(parameterTypes);
        if (unit.thisType().isMethodImplemented(m.getName(), typeParameters) || m.getDeclaringClass().equals(CompiledScript.class)) {
            return;
        }
        Type[] toCall = new Type[parameterTypes.length + 1];
        toCall[0] = Type.getType(trait.traitInterface());
        for (int i = 1; i < toCall.length; i++) {
            toCall[i] = parameterTypes[i - 1].toASMType();
        }
        final UnfinishedMethodDescriptor implementingMethod =
                new UnfinishedMethodDescriptor(null, unit.thisType(), m.getName(), parameterTypes, JavaClassWrapper.wrapperFor(m
                        .getReturnType()));
        unit.addMethod(implementingMethod);
        unit.thisType().writeMethod(-1, m.getName(), implementingMethod.parameterTypes());
        MethodVisitor visitor =
                writer.visitMethod(removeModifier(m.getModifiers(), ACC_ABSTRACT), m.getName(), implementingMethod.toDescription(), null,
                        null);
        visitor.visitCode();

        for (int i = 0, offset = 0; i < toCall.length; i++) {
            visitor.visitVarInsn(toCall[i].getOpcode(Opcodes.ILOAD), offset);
            offset += toCall[i].getSize();
        }

        final Type returnType = Type.getType(m.getReturnType());
        // we assume such a method exists
        visitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(trait.implementor()), m.getName(),
                Type.getMethodDescriptor(returnType, toCall));
        if (!(m.getReturnType() == void.class)) {
            visitor.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
        } else {
            visitor.visitInsn(Opcodes.RETURN);
        }
        visitor.visitMaxs(0, 0);
        visitor.visitEnd();
    }

    /**
     * Removes a modifier
     * 
     * @param modifiers the modifiers
     * @param toRemove the modifier to remove
     * @return int
     */
    private static int removeModifier(int modifiers, int toRemove) {
        return modifiers & ~toRemove;
    }

    /**
     * Converts a {@link Class} array to a {@link TypeWrapper} array
     * 
     * @param parameterTypes array
     * @return {@link TypeWrapper}
     */
    private TypeWrapper[] toTypeWrappers(Class<?>[] parameterTypes) {
        TypeWrapper[] array = new TypeWrapper[parameterTypes.length];
        for (int i = 0; i < array.length; i++) {
            array[i] = JavaClassWrapper.wrapperFor(parameterTypes[i]);
        }
        return array;
    }

    /**
     * Adds all fields from the given traits
     * 
     * @param traits the list of traits
     * @param unit {@link CompilationUnit}
     * @param writer {@link ClassVisitor}
     */
    private void addFieldsFromTraits(List<? extends LoadedTrait> traits, CompilationUnit unit, ClassVisitor writer) {
        LinkedList<LoadedTrait> allTraits = new LinkedList<LoadedTrait>(traits);
        while (!allTraits.isEmpty()) {
            final LoadedTrait trait = allTraits.removeFirst();
            for (FieldDescriptor fd : trait.instanceFields()) {
                if (unit.getField(fd.fieldName()) == null) {
                    unit.addField(fd.fieldName(), fd.fieldType(), fd.asmModifiers());
                    FieldVisitor f = writer.visitField(fd.asmModifiers(), fd.fieldName(), fd.fieldType().typeDescription(), null, null);
                    f.visitEnd();
                }
            }
            allTraits.addAll(trait.implementedTraits());
        }
    }

    /**
     * Adds interfaces from the {@link CompilationSource} (including traits)
     * 
     * @param source the {@link CompilationSource}
     * @param interfaces the list of interfaces
     */
    public void addInterfacesFromCompilationSource(CompilationSource source, Set<String> interfaces) {
        for (Class<?> implemented : source.implementedInterfaces()) {
            interfaces.add(Type.getInternalName(implemented));
        }
        for (LoadedTrait trait : source.implementedTraits()) {
            addTraitInterfaceRecurse(trait, interfaces);
        }
    }

    /**
     * Adds all interfaces from the given trait
     * 
     * @param trait the {@link LoadedTrait}
     * @param interfaces the set of interfaces
     */
    private void addTraitInterfaceRecurse(LoadedTrait trait, Set<String> interfaces) {
        interfaces.add(Type.getInternalName(trait.traitInterface()));
        for (LoadedTrait parent : trait.implementedTraits()) {
            addTraitInterfaceRecurse(parent, interfaces);
        }
    }

    /**
     * Extracts all interfaces implemented by a type
     * 
     * @param unit {@link CompilationUnit}
     * @return {@link Set} of interface internal names
     */
    public Set<String> extractInterfaces(CompilationUnit unit) {
        Set<String> interfaces = new HashSet<String>();
        List<? extends TypeWrapper> list = unit.thisType().interfaces();
        for (TypeWrapper typeWrapper : list) {
            interfaces.add(typeWrapper.internalName());
        }
        return interfaces;
    }

    /**
     * @param writer the classWriter
     * @param debugOptions the debug options
     */
    private void createDebugFlags(ClassVisitor writer, Set<DebugOptions> debugOptions) {
        MethodVisitor mv = writer.visitMethod(ACC_PUBLIC, "debugFlags", "()I", null, null);
        int flags = 0;
        for (DebugOptions option : debugOptions) {
            flags |= 1 << option.ordinal();
        }
        mv.visitCode();
        BytecodeHelper.pushIntConst(flags, mv);
        mv.visitInsn(IRETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    /**
     * Cria os campos constates como static final. Aceita apenas campos primitivos (e os wrappers), enums e Strings
     * 
     * @param writer {@link ClassVisitor}
     * @param unit {@link CompilationUnit}
     * @param constants {@link Map}
     * @return {@link List} de {@link BytecodeGenerator}
     */
    private List<BytecodeGenerator> createConstants(ClassVisitor writer, CompilationUnit unit, Map<String, Object> constants) {
        List<BytecodeGenerator> list = new ArrayList<BytecodeGenerator>();
        for (Entry<String, Object> entry : constants.entrySet()) {
            Object value = entry.getValue();
            Class<?> type;
            if (Primitive.isWrapperType(entry.getValue().getClass())) {
                type = unboxType(value.getClass());
                value = PrimitiveUtils.promoteToInteger(value);
            } else if (this.reflect.isEnumInstance(entry.getValue())) {
                final Class<?> enumType = this.reflect.getEnumType(entry.getValue());
                final String constantName = entry.getKey();
                final Field f = this.getEnumField(enumType, value);
                type = enumType;
                list.add(new BytecodeGenerator() {
                    public void generateBytecode(MethodVisitor visitor, CompilationUnit unit, CompilationState state) {
                        visitor.visitFieldInsn(GETSTATIC, Type.getInternalName(enumType), f.getName(), Type.getDescriptor(enumType));
                        visitor.visitFieldInsn(PUTSTATIC, unit.getThisClassDescriptor(), constantName, Type.getDescriptor(enumType));
                    }
                });
                value = null;
            } else if (entry.getValue() instanceof String) {
                type = String.class;
            } else {
                throw new IllegalArgumentException("cannot create a constant of type: " + entry.getValue().getClass());
            }
            unit.addStaticField(entry.getKey(), JavaClassWrapper.wrapperFor(type), ACC_STATIC | ACC_FINAL | ACC_PUBLIC);
            writer.visitField(ACC_STATIC + ACC_FINAL + ACC_PUBLIC, entry.getKey(), Type.getDescriptor(type), null, value);
        }
        return list;
    }

    /**
     * Devolve o {@link Field} que representa o valor de um {@link Enum}
     * 
     * @param enumType {@link Enum}
     * @param value o valor
     * @return {@link Field}
     */
    private Field getEnumField(Class<?> enumType, Object value) {
        Field[] fields = enumType.getFields();
        for (Field field : fields) {
            try {
                if (field.isEnumConstant() && field.get(null) == value) {
                    return field;
                }
            } catch (IllegalAccessException e) {
                throw new IllegalArgumentException("cannot access enum value: " + value + " on enum type: " + enumType);
            }
        }
        throw new IllegalArgumentException("cannot access enum value: " + value + " on enum type: " + enumType);
    }

    /**
     * {@inheritDoc}
     */
    public UnfinishedInnerType compileInnerClass(String clName, String simpleName, CompilationUnit unit, BSHBlock classBlock,
            Scope declaringScope, TypeWrapper superType, TypeWrapper[] interfaces, int modifiers) throws CompilationFailedException {
        ArrayList<TypeWrapper> implemented = new ArrayList<TypeWrapper>(Arrays.asList(interfaces));
        implemented.add(JavaClassWrapper.wrapperFor(CompiledScript.class));
        final CompilationUnit innerCompilationUnit =
                unit.createInnerType(clName, superType, implemented, declaringScope, modifiers, simpleName);
        if (simpleName == null) { // anonymous
            this.copyConstructorFromSuperClass(innerCompilationUnit);
        } else {
            declaringScope.addTypeAlias(simpleName, innerCompilationUnit.thisType());
        }
        this.doCompileInnerClass(innerCompilationUnit, simpleName, classBlock, unit);
        return (UnfinishedInnerType) innerCompilationUnit.thisType();
    }

    /**
     * Copies constructors from superclass, prepending the external variables
     * 
     * @param innerCompilationUnit the {@link CompilationUnit}
     */
    public void copyConstructorFromSuperClass(final CompilationUnit innerCompilationUnit) {
        for (List<? extends TypeWrapper> ctor : innerCompilationUnit.thisType().superType().visibleConstructors()) {
            final Parameter[] params = new Parameter[ctor.size()];
            for (int i = 0; i < params.length; i++) {
                params[i] = new Parameter(ctor.get(i), "arg" + i);
            }
            innerCompilationUnit.thisType().addConstructorWithBody(params, new BytecodeGenerator() {

                public void generateBytecode(MethodVisitor visitor, CompilationUnit unit, CompilationState state)
                        throws CompilationFailedException {
                    int stack = state.getStackSize();
                    visitor.visitVarInsn(ALOAD, 0);
                    state.pushStack(innerCompilationUnit.thisType());
                    TypeWrapper[] types = new TypeWrapper[params.length];
                    for (int i = 0; i < params.length; i++) {
                        state.currentScope().getAccessor(params[i].getName());
                        ObjectAccessor localAcc = state.currentScope().getAccessor(params[i].getName());
                        localAcc.loadObject(visitor, state);
                        types[i] = params[i].getType();
                    }
                    MethodDescriptor superCtor =
                            new UnfinishedMethodDescriptor(null, innerCompilationUnit.thisType().superType(), "<init>", types,
                                    JavaClassWrapper.VOID_WRAPPER);
                    superCtor.writeInvoke(visitor, state);
                    state.assertStackSize(stack);
                }
            });
        }
    }

    /**
     * Actually compiles the inner class definition.
     * 
     * @param innerCompilationUnit the inner {@link CompilationUnit}
     * @param simpleName the simple name. May be null if it's an annonymous
     * @param classBlock the class body
     * @param outer the outer compilation unit
     * @throws CompilationFailedException e
     */
    private void doCompileInnerClass(final CompilationUnit innerCompilationUnit, String simpleName, BSHBlock classBlock,
            CompilationUnit outer) throws CompilationFailedException {
        List<DelayedMethod> methods = new ArrayList<DelayedMethod>();
        List<BytecodeGenerator> initializers = new ArrayList<BytecodeGenerator>();
        List<BytecodeGenerator> staticInitializers = new ArrayList<BytecodeGenerator>();
        for (int i = 0; i < classBlock.jjtGetNumChildren(); i++) {
            SimpleNode node = classBlock.getChild(i);
            if (node instanceof BSHTypedVariableDeclaration) {
                this.addField(innerCompilationUnit, initializers, node, staticInitializers);
            } else if (node instanceof BSHBlock) {
                this.addInitializerBlock(initializers, node);
            } else if (node instanceof BSHMethodDeclaration) {
                BSHMethodDeclaration methodDeclaration = (BSHMethodDeclaration) node;
                final MethodDeclarationParser methodSignatureParser =
                        new MethodDeclarationParser(methodDeclaration, innerCompilationUnit, innerCompilationUnit.getGlobalScope());
                if (methodSignatureParser.isConstructor()) { // its a constructor
                    this.addInnerClassConstructor(innerCompilationUnit, methodSignatureParser);
                } else {
                    methods.add(new DelayedMethod(methodDeclaration, innerCompilationUnit.getGlobalScope()));
                }
            } else if (node instanceof BSHClassDeclaration) {
                innerCompilationUnit.compileNode(node, null, new CompilationState(innerCompilationUnit));
            }
        }
        if (innerCompilationUnit.thisType().getUnfinishedConstructors().isEmpty()) {
            // must add an empty constructor
            innerCompilationUnit.thisType().addConstructorWithBody(new Parameter[]{}, new BytecodeGenerator() {

                public void generateBytecode(MethodVisitor visitor, CompilationUnit unit, CompilationState state)
                        throws CompilationFailedException {
                    unit.thisType().loadThis(visitor, state);
                    MethodDescriptor superCtor =
                            new UnfinishedMethodDescriptor(null, innerCompilationUnit.thisType().superType(), "<init>", new TypeWrapper[0],
                                    JavaClassWrapper.VOID_WRAPPER);
                    superCtor.writeInvoke(visitor, state);
                    state.assertStackSize(0);
                }
            });
        }

        ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);

        Set<String> interfaces = this.extractInterfaces(innerCompilationUnit);

        writer.visit(Opcodes.V1_5, ACC_PUBLIC + ACC_SUPER, innerCompilationUnit.thisType().internalName(), null, innerCompilationUnit
                .thisType().superType().internalName(), interfaces.toArray(new String[interfaces.size()]));
        if (innerCompilationUnit.thisType().getSourceCodeFileName() != null) {
            writer.visitSource(innerCompilationUnit.thisType().getSourceCodeFileName(), null);
        }

        // writer.visitInnerClass(innerCompilationUnit.thisType().internalName(), null, simpleName, ACC_PUBLIC);

        this.addMethodSignatures(methods, innerCompilationUnit);
        this.createScriptBody(writer, innerCompilationUnit, Collections.<SimpleNode> emptyList(), null);
        this.createDeclaredMethods(writer, methods, innerCompilationUnit);

        this.createConstructors(innerCompilationUnit, writer, initializers, outer);
        // createStaticInitializers(innerCompilationUnit, staticInitializers, writer);

        for (FieldDescriptor fd : innerCompilationUnit.thisType().visibleFields()) {
            FieldVisitor visitor =
                    writer.visitField(ACC_PUBLIC | ACC_SYNTHETIC | fd.asmModifiers(), fd.fieldName(), fd.fieldType().typeDescription(),
                            null, null);
            visitor.visitEnd();
        }

        this.createStaticCallsiteCache(writer, innerCompilationUnit.thisType().internalName(), staticInitializers, innerCompilationUnit);

        CompiledScriptPrototypeWriter prototype = new CompiledScriptPrototypeWriter();
        prototype.write(writer, new CompilationState(innerCompilationUnit));

        writer.visitEnd();
        outer.addInnerClassDefinition(writer.toByteArray(), (UnfinishedInnerType) innerCompilationUnit.thisType());
    }

    /**
     * Adds a contructor definition to an inner class
     * 
     * @param innerCompilationUnit the inner {@link CompilationUnit}
     * @param methodSignatureParser the constructor declaration
     */
    public void addInnerClassConstructor(final CompilationUnit innerCompilationUnit, final MethodDeclarationParser methodSignatureParser) {
        innerCompilationUnit.thisType().addConstructorWithBody(methodSignatureParser.getParams(), new BytecodeGenerator() {
            public void generateBytecode(MethodVisitor visitor, CompilationUnit unit, CompilationState state)
                    throws CompilationFailedException {
                // must check for a super(...) or this( .... )
                if (!this.hasConstructorCall(methodSignatureParser.getMethodBlock())) {
                    // add empty constructor call

                    if (!innerCompilationUnit.thisType().superType().visibleConstructors().contains(Collections.emptyList())) {
                        throw new CompilationFailedException(methodSignatureParser.getMethodBlock().getLineNumber(),
                                "Cannot find default constructor on type: " + innerCompilationUnit.thisType().superType());
                    }
                    visitor.visitVarInsn(ALOAD, 0);
                    visitor.visitMethodInsn(INVOKESPECIAL, innerCompilationUnit.thisType().superType().internalName(), "<init>", "()V");
                }
                innerCompilationUnit.getNodeCompiler(methodSignatureParser.getMethodBlock()).compile(visitor, state);
            }

            private boolean hasConstructorCall(SimpleNode node) {
                if (node instanceof BSHMethodInvocation) {
                    BSHMethodInvocation inv = (BSHMethodInvocation) node;
                    return "this".equals(inv.getNameNode().text) || "super".equals(inv.getNameNode().text);
                } else if (node.jjtGetNumChildren() > 0) {
                    return this.hasConstructorCall(node.getChild(0));
                }
                return false;
            }
        });
    }

    /**
     * Creates the declared constructors for inner classes. The constructors are extracted from
     * {@link UnfinishedInnerType#getUnfinishedConstructors()}
     * 
     * @param innerCompilationUnit the {@link CompilationUnit}
     * @param writer the {@link ClassVisitor} to write the classes to
     * @param initializers the initializers to put on every constructor
     * @param outer the outer compilationunit
     * @throws CompilationFailedException e
     */
    private void createConstructors(CompilationUnit innerCompilationUnit, ClassVisitor writer, List<BytecodeGenerator> initializers,
            CompilationUnit outer) throws CompilationFailedException {
        InnerTypeGlobalScope globalScope = (InnerTypeGlobalScope) innerCompilationUnit.getGlobalScope();
        List<TypeWrapper> prepended = new ArrayList<TypeWrapper>(1 + globalScope.getExternalVariableMapping().size());
        prepended.add(outer.thisType());
        for (String varName : globalScope.getExternalVariableMapping().keySet()) {
            prepended.add(globalScope.getAccessor(varName).type());
        }

        UnfinishedInnerType innerType = (UnfinishedInnerType) innerCompilationUnit.thisType();
        innerType.setExternalVariables(new ArrayList<String>(globalScope.getExternalVariableMapping().keySet()));

        for (DeclaredConstructor ctr : innerCompilationUnit.thisType().getUnfinishedConstructors()) {
            TypeWrapper[] actualTypeParameters = new TypeWrapper[prepended.size() + ctr.getParameters().length];
            for (int i = 0; i < prepended.size(); i++) {
                actualTypeParameters[i] = prepended.get(i);
            }
            for (int i = 0; i < ctr.getParameters().length; i++) {
                actualTypeParameters[i + prepended.size()] = ctr.getParameters()[i].getType();
            }
            // actualTypeParameters.toString();d
            innerType.addConstructor(Arrays.asList(actualTypeParameters));

            CompilationState state = new CompilationState(innerCompilationUnit);
            UnfinishedMethodDescriptor constructorDescriptor =
                    new UnfinishedMethodDescriptor(new TypeWrapper[]{}, innerType, "<init>", actualTypeParameters,
                            JavaClassWrapper.VOID_WRAPPER);
            MethodScope methodScope = state.newMethodScope(constructorDescriptor, innerCompilationUnit.getGlobalScope());
            state.assertStackSize(0);
            MethodVisitor visitor = writer.visitMethod(ACC_PUBLIC, "<init>", constructorDescriptor.toDescription(), null, null);
            visitor.visitCode();
            methodScope.addMethodParameter(innerType.getEnclosingInstanceField().fieldName(), innerType.getEnclosingInstanceField()
                    .fieldType());

            ThisFieldObjectAcessor acc = new ThisFieldObjectAcessor(innerType, innerType.getEnclosingInstanceField());
            acc.preStore(visitor, state);
            visitor.visitVarInsn(ALOAD, 1);
            state.pushStack(innerType.getEnclosingInstanceField().fieldType());
            acc.storeObject(visitor, state);
            state.assertStackSize(0);

            for (Entry<String, String> var : globalScope.getExternalVariableMapping().entrySet()) {
                FieldDescriptor regularField = innerType.getField(var.getValue());
                ThisFieldObjectAcessor fieldAccessor = new ThisFieldObjectAcessor(innerType, regularField);
                LocalObjectAccessor localAcc =
                        new LocalObjectAccessor(methodScope.addMethodParameter(var.getValue(), regularField.fieldType()));
                fieldAccessor.preStore(visitor, state);
                localAcc.loadObject(visitor, state);
                fieldAccessor.storeObject(visitor, state);
                state.assertStackSize(0);
            }
            for (Parameter param : ctr.getParameters()) {
                methodScope.addMethodParameter(param.getName(), param.getType());
            }
            ctr.getConstructorBody().generateBytecode(visitor, innerCompilationUnit, state);

            for (BytecodeGenerator init : initializers) {
                init.generateBytecode(visitor, innerCompilationUnit, state);
                state.assertStackSize(0);
            }
            state.assertStackSize(0);

            this.initializeSyntheticFields(innerCompilationUnit, visitor);

            visitor.visitInsn(RETURN);
            visitor.visitMaxs(0, 0);
            visitor.visitEnd();
            state.popScope();
        }
    }

    /**
     * Adds a node compilation to a list of initializers
     * 
     * @param initializers the list of initializers
     * @param node the node to be compiled
     */
    public void addInitializerBlock(List<BytecodeGenerator> initializers, SimpleNode node) {
        final BSHBlock block = (BSHBlock) node;
        initializers.add(new BytecodeGenerator() {

            public void generateBytecode(MethodVisitor visitor, CompilationUnit unit, CompilationState state)
                    throws CompilationFailedException {
                int size = state.getStackSize();
                unit.compileNode(block, visitor, state);
                // stack size should never change
                state.assertStackSize(size);
            }
        });
    }

    /**
     * Adds an instance variable - aka instance field - to the inner class. Optionally, compiles initialization code and puts in the given
     * list
     * 
     * @param innerCompilationUnit the {@link CompilationUnit}
     * @param initializers list to be filled with instance field initializers
     * @param node the node that contains the variable(s) declaration(s)
     * @param staticInitializer list to be filled with static field initializers
     * @throws CompilationFailedException e
     */
    public void addField(CompilationUnit innerCompilationUnit, List<BytecodeGenerator> initializers, SimpleNode node,
            List<BytecodeGenerator> staticInitializer) throws CompilationFailedException {
        BSHTypedVariableDeclaration typedDeclaration = (BSHTypedVariableDeclaration) node;
        int modifiers = this.readModifiers(typedDeclaration);
        TypedVariableDeclarationCompiler tmp = new TypedVariableDeclarationCompiler(typedDeclaration, innerCompilationUnit);
        final TypeWrapper varType = tmp.variableType(innerCompilationUnit.getGlobalScope());
        for (int j = 1; j < typedDeclaration.jjtGetNumChildren(); j++) {
            final BSHVariableDeclarator declarator = (BSHVariableDeclarator) typedDeclaration.getChild(j);
            FieldDescriptor field = innerCompilationUnit.addField(declarator.name, varType, modifiers);
            if (declarator.jjtGetNumChildren() == 1) { // has initializer
                if (field.isStatic()) {
                    staticInitializer.add(new BytecodeGenerator() {

                        public void generateBytecode(MethodVisitor visitor, CompilationUnit unit, CompilationState state)
                                throws CompilationFailedException {
                            int size = state.getStackSize();
                            ObjectAccessor accessor = unit.getFieldAcessor(declarator.name);
                            accessor.preStore(visitor, state);
                            unit.compileNode(declarator.getChild(0), visitor, state);
                            unit.getCompilerManager().coerceStackTopTo(visitor, varType, state);
                            accessor.storeObject(visitor, state);
                            state.assertStackSize(size);
                        }
                    });
                } else {
                    initializers.add(new BytecodeGenerator() {

                        public void generateBytecode(MethodVisitor visitor, CompilationUnit unit, CompilationState state)
                                throws CompilationFailedException {
                            int size = state.getStackSize();
                            ObjectAccessor accessor = unit.getFieldAcessor(declarator.name);
                            accessor.preStore(visitor, state);
                            state.assertStackSize(1 + size);
                            unit.compileNode(declarator.getChild(0), visitor, state);
                            state.assertStackSize(2 + size);
                            unit.getCompilerManager().coerceStackTopTo(visitor, varType, state);
                            state.assertStackSize(2 + size);
                            accessor.storeObject(visitor, state);
                            state.assertStackSize(size);

                        }
                    });
                }
            }
        }
    }

    /**
     * Tries to read modifiers from a {@link BSHTypedVariableDeclaration}
     * 
     * @param typedDeclaration the {@link BSHTypedVariableDeclaration}
     * @return modifiers as ASM needs them
     */
    private int readModifiers(BSHTypedVariableDeclaration typedDeclaration) {
        if (typedDeclaration.modifiers == null) {
            return 0;
        }
        int mod = 0;
        if (typedDeclaration.modifiers.hasModifier("volatile")) {
            mod |= Opcodes.ACC_VOLATILE;
        }
        if (typedDeclaration.modifiers.hasModifier("final")) {
            mod |= Opcodes.ACC_FINAL;
        }
        if (typedDeclaration.modifiers.hasModifier("transient")) {
            mod |= Opcodes.ACC_TRANSIENT;
        }
        if (typedDeclaration.modifiers.hasModifier("static")) {
            mod |= Opcodes.ACC_STATIC;
        }
        return mod;
    }

    /**
     * {@inheritDoc}
     */
    public Trait compileAsTrait(CompilationSource source, CompilationOptions options) throws ParseException, CompilationFailedException {
        final String interfaceName = source.internalName().replaceAll("[.]", "/");
        List<BytecodeGenerator> constructorCode = new LinkedList<BytecodeGenerator>();
        List<BytecodeGenerator> staticInitializers = new LinkedList<BytecodeGenerator>();
        List<FieldDescriptor> fields = new LinkedList<FieldDescriptor>();
        List<BSHMethodDeclaration> declaredMethods = new LinkedList<BSHMethodDeclaration>();
        // we will first parse and create the interface
        final int classModifiers = ACC_PUBLIC | ACC_INTERFACE | ACC_ABSTRACT;
        CompilationUnit unit =
                createCompilationUnit(this.createUnfinishedType(source, classModifiers), source, options, interfaceName, classModifiers);
        ClassWriter writer = initializeClassWriter(source, unit, classModifiers);
        List<SimpleNode> allNodes = new ArrayList<SimpleNode>();
        Parser parser = new Parser(source.openReader());
        boolean eof = false;
        while (!eof) {
            eof = parser.Line();
            if (parser.jjtree.nodeArity() > 0) {
                final SimpleNode n = (SimpleNode) parser.jjtree.popNode();
                allNodes.add(n);
                if (n instanceof BSHClassDeclaration) {
                    addUnfinishedInnerClassDeclaration(unit, (BSHClassDeclaration) n, unit.getGlobalScope());
                }
            }
        }

        for (SimpleNode n : allNodes) {
            if (n instanceof BSHTypedVariableDeclaration) {
                FieldDescriptor fd = parseVariable((BSHTypedVariableDeclaration) n, unit, constructorCode, staticInitializers, writer);
                if (fd != null) {
                    fields.add(fd);
                }
            } else if (n instanceof BSHImportDeclaration) {
                this.addImport(unit, (BSHImportDeclaration) n);
            } else if (n instanceof BSHMethodDeclaration) {
                BSHMethodDeclaration methodDeclaration = (BSHMethodDeclaration) n;
                final MethodDeclarationParser methodSignatureParser =
                        new MethodDeclarationParser(methodDeclaration, unit, unit.getGlobalScope());
                declaredMethods.add(methodDeclaration);
                if (methodSignatureParser.isConstructor()) { // its a constructor
                    throw new CompilationFailedException(n.getLineNumber(), "Cannot define a constructor inside an interface");
                } else {
                    writer.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT, methodDeclaration.name,
                            methodSignatureParser.getMethodDescriptor().toDescription(), null, null).visitEnd();
                }
                unit.thisType().addMethod(methodSignatureParser.getMethodDescriptor());
            } else if (n instanceof BSHClassDeclaration) {
                ClassDeclarationNodeCompiler compiler = (ClassDeclarationNodeCompiler) unit.getNodeCompiler(n);
                compiler.compile(null, new CompilationState(unit));
            } else {
                constructorCode.add(ClassDeclarationNodeCompiler.createInitializerCode(n));
            }
        }

        writer.visitEnd();
        byte[] traitInterfaceBytecode = writer.toByteArray();

        final String implementorName = interfaceName + "$Impl";
        CompilationUnit implementorCompilationUnit =
                createCompilationUnit(unit.thisType(), source, options, implementorName, ACC_PUBLIC | ACC_SUPER);
        ClassWriter implementorWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        Set<String> interfaces = this.extractInterfaces(implementorCompilationUnit);
        addInterfacesFromCompilationSource(source, interfaces);
        implementorWriter.visit(Opcodes.V1_5, ACC_PUBLIC | ACC_SUPER, implementorName, null, Type.getInternalName(Object.class),
                interfaces.toArray(new String[interfaces.size()]));
        if (implementorCompilationUnit.thisType().getSourceCodeFileName() != null) {
            implementorWriter.visitSource(implementorCompilationUnit.thisType().getSourceCodeFileName(), null);
        }
        implementorCompilationUnit.getGlobalScope().copyTypeAliases(unit.getGlobalScope());

        CompilationState state = new CompilationState(unit);

        for (BSHMethodDeclaration bshMethodDeclaration : declaredMethods) {
            createImplementationMethod(unit.thisType(), implementorCompilationUnit, implementorWriter, state, new DelayedMethod(
                    bshMethodDeclaration, state.currentScope()), options);
        }
        implementorWriter.visitEnd();
        Set<String> wildCards = unit.getGlobalScope().getWildCards();
        final Set<Entry<String, TypeWrapper>> imports = unit.getGlobalScope().getImports().entrySet();
        Map<String, Class<?>> fixedImports = new HashMap<String, Class<?>>(imports.size());
        for (Entry<String, TypeWrapper> entry : imports) {
            if (entry.getValue() instanceof JavaClassWrapper) {
                fixedImports.put(entry.getKey(), JavaClassWrapper.unwrapJavaClass(entry.getValue()));
            }
        }
        final Map<String, byte[]> interfaceInnerTypes = unit.getDependentTypes();
        interfaceInnerTypes.putAll(implementorCompilationUnit.getDependentTypes());
        return new TraitImpl(constructorCode, fields, source.implementedTraits(), traitInterfaceBytecode, implementorWriter.toByteArray(),
                interfaceName, implementorName, fixedImports, wildCards, interfaceInnerTypes);
    }

    /**
     * Creates the implementation method for a trait
     * 
     * @param interfaceType {@link UnfinishedType}
     * @param unit the {@link CompilationUnit}
     * @param implementorWriter {@link ClassVisitor}
     * @param state the {@link CompilationState}
     * @param delayed the method to be implemented
     * @param options the {@link CompilationOptions}
     * @throws CompilationFailedException e
     */
    public void createImplementationMethod(UnfinishedType interfaceType, CompilationUnit unit, ClassVisitor implementorWriter,
            CompilationState state, DelayedMethod delayed, CompilationOptions options) throws CompilationFailedException {
        final MethodDeclarationParser methodSignatureParser =
                new MethodDeclarationParser(delayed.getMethodDeclaration(), unit, unit.getGlobalScope());
        List<SimpleNode> nodes = new ArrayList<SimpleNode>();
        List<DelayedMethod> innerMethods = new LinkedList<DelayedMethod>();
        final UnfinishedMethodDescriptor methodDescriptor = methodSignatureParser.getMethodDescriptor();
        MethodScope methodScope = state.newMethodScope(methodDescriptor, unit.getGlobalScope());
        TypeWrapper returnType = methodDescriptor.returnType();
        if (methodSignatureParser.getMethodBlock() != null && methodSignatureParser.getMethodBlock().children != null) {
            for (Node node : methodSignatureParser.getMethodBlock().children) {
                if (node instanceof BSHClassDeclaration) {
                    addUnfinishedInnerClassDeclaration(unit, (BSHClassDeclaration) node, methodScope);
                } else if (node instanceof BSHMethodDeclaration) {
                    innerMethods.add(new DelayedMethod((BSHMethodDeclaration) node, methodScope));
                } else {
                    nodes.add((SimpleNode) node);
                }
            }
        }
        final Parameter[] originalParams = methodSignatureParser.getParams();
        Type[] parameters = new Type[originalParams.length + 1];
        parameters[0] = Type.getType(interfaceType.typeDescription());
        for (int i = 0; i < originalParams.length; i++) {
            parameters[i + 1] = originalParams[i].getType().toASMType();
            methodScope.addMethodParameter(originalParams[i].getName(), originalParams[i].getType());
        }
        MethodVisitor mv =
                implementorWriter.visitMethod(methodDescriptor.modifiers() | ACC_STATIC, methodDescriptor.methodName(),
                        Type.getMethodDescriptor(methodDescriptor.returnType().toASMType(), parameters), null, null);
        mv.visitCode();
        methodScope.installStartLabel(mv, options);
        SimpleNode last = null;
        for (SimpleNode node : nodes) {
            last = node;
            popStackIfNeeded(mv, state);
            unit.compileNode(node, mv, state);
        }
        methodScope.installEndLabel(mv, options);
        if (JavaClassWrapper.VOID_WRAPPER.equals(returnType)) {
            popStackIfNeeded(mv, state);
            state.assertEmptyStack();
            mv.visitInsn(RETURN);
        } else {
            if (state.getStackSize() == 0) {
                // push null
                if (!(last instanceof BSHReturnStatement)) {
                    mv.visitInsn(ACONST_NULL);
                    state.pushStack(TypeWrapper.NULL_TYPE_WRAPPER);
                } else {
                    state.popScope();
                    mv.visitMaxs(0, 0);
                    mv.visitEnd();
                    // System.out.println(visitor.text);
                    return;
                }
            } else {
                state.assertStackSize(1);
                this.coerceToType(mv, state.peekStack(), returnType, state);
            }
            state.assertStackSize(1);
            state.popStack();
            mv.visitInsn(returnType.toASMType().getOpcode(Opcodes.IRETURN));
        }
        state.popScope();
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    /**
     * Parses a variable declaration and puts as a field in the compilation unit
     * 
     * @param m the variable declaration node
     * @param unit {@link CompilationUnit}
     * @param constructorCode list of initializers
     * @param staticInitializers list of static initializers
     * @param classVisitor {@link ClassVisitor}
     * @return {@link FieldDescriptor}
     * @throws CompilationFailedException e
     */
    private FieldDescriptor parseVariable(BSHTypedVariableDeclaration m, CompilationUnit unit, List<BytecodeGenerator> constructorCode,
            List<BytecodeGenerator> staticInitializers, ClassVisitor classVisitor) throws CompilationFailedException {
        int modifiers = this.readModifiers(m) | ACC_PUBLIC;
        TypedVariableDeclarationCompiler tmp = new TypedVariableDeclarationCompiler(m, unit);
        final TypeWrapper varType = tmp.variableType(unit.getGlobalScope());
        for (int i = 0; i < m.jjtGetNumChildren(); i++) {
            if (!(m.getChild(i) instanceof BSHVariableDeclarator)) {
                continue;
            }
            final BSHVariableDeclarator declarator = (BSHVariableDeclarator) m.getChild(i);
            FieldDescriptor fd = new FieldDescriptorImpl(unit.thisType(), varType, declarator.name);
            if (fd.isStatic()) {
                unit.addField(fd.fieldName(), varType, modifiers);
                classVisitor.visitField(modifiers, fd.fieldName(), fd.fieldType().typeDescription(), null, null).visitEnd();
                if (declarator.jjtGetNumChildren() == 1) {
                    staticInitializers.add(new BytecodeGenerator() {

                        public void generateBytecode(MethodVisitor visitor, CompilationUnit unit, CompilationState state)
                                throws CompilationFailedException {
                            int size = state.getStackSize();
                            ObjectAccessor accessor = unit.getFieldAcessor(declarator.name);
                            accessor.preStore(visitor, state);
                            unit.compileNode(declarator.getChild(0), visitor, state);
                            unit.getCompilerManager().coerceStackTopTo(visitor, varType, state);
                            accessor.storeObject(visitor, state);
                            state.assertStackSize(size);
                        }
                    });
                }
                return null;
            } else if (declarator.jjtGetNumChildren() == 1) {
                constructorCode.add(new BytecodeGenerator() {

                    public void generateBytecode(MethodVisitor visitor, CompilationUnit unit, CompilationState state)
                            throws CompilationFailedException {
                        int size = state.getStackSize();
                        ObjectAccessor accessor = unit.getFieldAcessor(declarator.name);
                        accessor.preStore(visitor, state);
                        state.assertStackSize(1 + size);
                        unit.compileNode(declarator.getChild(0), visitor, state);
                        state.assertStackSize(2 + size);
                        unit.getCompilerManager().coerceStackTopTo(visitor, varType, state);
                        state.assertStackSize(2 + size);
                        accessor.storeObject(visitor, state);
                        state.assertStackSize(size);
                    }
                });
            }
            return fd;
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public Trait compileAsTrait(CompilationSource source) throws ParseException, CompilationFailedException {
        return this.compileAsTrait(source, this.compilationOptions);
    }
}
